package com.xuecheng.manage_cms.service.impl;

import com.alibaba.fastjson.JSON;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSDownloadStream;
import com.mongodb.client.gridfs.model.GridFSFile;
import com.xuecheng.framework.domain.cms.CmsConfig;
import com.xuecheng.framework.domain.cms.CmsPage;
import com.xuecheng.framework.domain.cms.CmsSite;
import com.xuecheng.framework.domain.cms.CmsTemplate;
import com.xuecheng.framework.domain.cms.request.QueryPageRequest;
import com.xuecheng.framework.domain.cms.response.CmsCode;
import com.xuecheng.framework.domain.cms.response.CmsPageResult;
import com.xuecheng.framework.exception.ExceptionCast;
import com.xuecheng.framework.model.response.CommonCode;
import com.xuecheng.framework.model.response.QueryResponseResult;
import com.xuecheng.framework.model.response.QueryResult;
import com.xuecheng.framework.model.response.ResponseResult;
import com.xuecheng.manage_cms.config.RabbitmqConfig;
import com.xuecheng.manage_cms.dao.CmsConfigRepository;
import com.xuecheng.manage_cms.dao.CmsPageRepository;
import com.xuecheng.manage_cms.dao.CmsSiteRepository;
import com.xuecheng.manage_cms.dao.CmsTemplateRepository;
import com.xuecheng.manage_cms.service.PageService;
import freemarker.cache.StringTemplateLoader;
import freemarker.cache.TemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.bson.types.ObjectId;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsResource;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@Service
public class PageServiceImpl implements PageService {
    @Autowired
    private CmsPageRepository cmsPageRepository;
    @Autowired
    private CmsSiteRepository cmsSiteRepository;
    @Autowired
    private CmsTemplateRepository cmsTemplateRepository;
    @Autowired
    private CmsConfigRepository cmsConfigRepository;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private GridFsTemplate gridFsTemplate;
    @Autowired
    private GridFSBucket gridFSBucket;
    @Autowired
    RabbitTemplate rabbitTemplate;
    @Override
    public QueryResponseResult findList(int page, int size, QueryPageRequest queryPageRequest) {
        if(queryPageRequest == null){
            queryPageRequest = new QueryPageRequest();
        }
        CmsPage cmsPage =  new CmsPage();
        if(StringUtils.isNotEmpty(queryPageRequest.getSiteId())){
            cmsPage.setSiteId(queryPageRequest.getSiteId());
        }
        if(StringUtils.isNotEmpty(queryPageRequest.getTemppateId())){
            cmsPage.setTemplateId(queryPageRequest.getTemppateId());
        }
        if(StringUtils.isNotEmpty(queryPageRequest.getPageAliase())){
            cmsPage.setPageAliase(queryPageRequest.getPageAliase());
        }
        ExampleMatcher exampleMatcher = ExampleMatcher.matching().withMatcher("pageAliase", ExampleMatcher.GenericPropertyMatchers.contains());
        //创造条件实例
        Example<CmsPage> example = Example.of(cmsPage,exampleMatcher);
        if(page <= 0){
            page = 1;
        }
        page -= 1;
        if(size <= 0){
            size = 10;
        }
        Pageable pageable = PageRequest.of(page,size);
        Page<CmsPage> cmsPages = cmsPageRepository.findAll(example,pageable);
        QueryResult<CmsPage> queryResult = new QueryResult<>();
        queryResult.setList(cmsPages.getContent());
        queryResult.setTotal(cmsPages.getTotalElements());
        QueryResponseResult queryResponseResult = new QueryResponseResult(CommonCode.SUCCESS,queryResult);
        return queryResponseResult;
    }

    @Override
    public QueryResponseResult getSiteList() {
        List<CmsSite> cmsSites = cmsSiteRepository.findAll();
        QueryResult<CmsSite> queryResult = new QueryResult<>();
        queryResult.setList(cmsSites);
        QueryResponseResult queryResponseResult = new QueryResponseResult(CommonCode.SUCCESS,queryResult);
        return queryResponseResult;
    }

    @Override
    public CmsPageResult add(CmsPage cmsPage) {
        //校验页面是否重复("名称,站点,访问路径唯一标识一条数据")
        CmsPage cmspage1 = cmsPageRepository.findByPageNameAndSiteIdAndPageWebPath(cmsPage.getPageName(), cmsPage.getSiteId(), cmsPage.getPageWebPath());
        if(null != cmspage1){
            ExceptionCast.cast(CmsCode.CMS_ADDPAGE_EXISTSNAME);
        }
        cmsPage.setPageId(null);//Id自动生成
        cmsPageRepository.save(cmsPage);
        return new CmsPageResult(CommonCode.SUCCESS,cmsPage);
    }

    @Override
    public QueryResponseResult getTemplateList() {
        List<CmsTemplate> cmsTemplates = cmsTemplateRepository.findAll();
        QueryResult<CmsTemplate> queryResult = new QueryResult<>();
        queryResult.setList(cmsTemplates);
        QueryResponseResult queryResponseResult = new QueryResponseResult(CommonCode.SUCCESS,queryResult);
        return queryResponseResult;
    }

    @Override
    public CmsPage findById(String id) {
        Optional<CmsPage> optional = cmsPageRepository.findById(id);
        if(optional.isPresent()){
            return optional.get();
        }
        return null;
    }

    @Override
    public CmsPageResult edit(String id, CmsPage cmsPage) {
        //查询
        CmsPage one = this.findById(id);
        if(null != one){
            one.setSiteId(cmsPage.getSiteId());
            one.setTemplateId(cmsPage.getTemplateId());
            one.setPageName(cmsPage.getPageName());
            one.setPageAliase(cmsPage.getPageAliase());
            one.setPageWebPath(cmsPage.getPageWebPath());
            one.setPagePhysicalPath(cmsPage.getPagePhysicalPath());
            one.setPageType(cmsPage.getPageType());
            one.setDataUrl(cmsPage.getDataUrl());

            cmsPageRepository.save(one);
            return new CmsPageResult(CommonCode.SUCCESS,one);
        }
        return new CmsPageResult(CommonCode.FAIL,null);
    }

    @Override
    public ResponseResult delete(String id) {
        CmsPage cmsPage = this.findById(id);
        if(null != cmsPage){
            cmsPageRepository.deleteById(id);
            return ResponseResult.SUCCESS();
        }
        return ResponseResult.FAIL();
    }

    @Override
    public CmsConfig findConfigById(String id) {
        Optional<CmsConfig> optional = cmsConfigRepository.findById(id);
        if(optional.isPresent()){
            CmsConfig cmsConfig = optional.get();
            return cmsConfig;
        }
        return null;
    }



    public String getPageHtml(String pageId){
        /***
         * 静态化程序获取页面的DataUrl
         * 静态化程序远程请求DataUrl获取数据模型。
         * 静态化程序获取页面的模板信息
         * 执行页面静态化
         */

        //获取模型数据
        Map model =  getModelByPageId(pageId);
        if(null == model){
            ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_DATAISNULL);
        }

        //获取模板内容
        String template = getTemplateById(pageId);
        if(StringUtils.isEmpty(template)){
            ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_TEMPLATEISNULL);
        }

        //执行静态化
        String htmlCotent = generateHtml(template,model);
        return htmlCotent;

    }

    /***
     * 执行静态化
     * @param template
     * @param model
     * @return
     */
    private String generateHtml(String template, Map model){
        Configuration configuration = new Configuration(Configuration.getVersion());
        //设置模板加载器
        StringTemplateLoader templateLoader = new StringTemplateLoader();
        templateLoader.putTemplate("template",template);
        configuration.setTemplateLoader(templateLoader);

        //获取模板对象
        Template mytemp = null;
        String content = null;
        try {
            mytemp = configuration.getTemplate("template");
            content = FreeMarkerTemplateUtils.processTemplateIntoString(mytemp, model);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return content;
    }

    private String getTemplateById(String pageId){
        CmsPage cmsPage = this.findById(pageId);
        if(null == cmsPage){
            ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_PAGENOTEXSITS);
        }
        String templateId = cmsPage.getTemplateId();
        if(StringUtils.isEmpty(templateId)){
            ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_TEMPLATEISNULL);
        }
        //获取模板信息
        Optional<CmsTemplate> optional = cmsTemplateRepository.findById(templateId);
        if(optional.isPresent()){
            CmsTemplate cmsTemplate = optional.get();
            //获取模板文件Id
            if(null == cmsTemplate){
                ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_TEMPLATEISNULL);
            }
            String templateFileId = cmsTemplate.getTemplateFileId();
            //获取模板内容
            GridFSFile gridFSFile = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(templateFileId)));
            //打开下载流对象
            GridFSDownloadStream gridFSDownloadStream = gridFSBucket.openDownloadStream(gridFSFile.getObjectId());
            //创建gridfsresource
            GridFsResource gridFsResource = new GridFsResource(gridFSFile,gridFSDownloadStream);
            //获取流中的数据
            String content = null;
            try {
                content = IOUtils.toString(gridFsResource.getInputStream(),"UTF-8");
            } catch (IOException e) {
                e.printStackTrace();
            }
            return content;
        }
        return null;
    }

    /***
     * 获取模型数据
     * @param pageId
     * @return
     */
    public Map getModelByPageId(String pageId) {
        CmsPage cmsPage = findById(pageId);
        if(null == cmsPage){
            ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_PAGENOTEXSITS);
        }
        String dataUrl = cmsPage.getDataUrl();
        if(StringUtils.isEmpty(dataUrl)){
            ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_DATAISNULL);
        }
        //调用restTemplate获取模型数据
        ResponseEntity<Map> forEntity = restTemplate.getForEntity(dataUrl, Map.class);
        Map body = forEntity.getBody();
        return body;

    }

    /***
     * 发布页面
     * @param pageId
     * @return
     */
    @Override
    public ResponseResult publishPage(String pageId) {
        //1.获取静态化html内容
        String pageHtml = this.getPageHtml(pageId);
        //2.保存内容到gridfs
        CmsPage cmsPage = saveHtml(pageId,pageHtml);
        //发送消息到MQ
        sendMsgToMq(pageId);
        return new ResponseResult(CommonCode.SUCCESS);
    }

    /***
     * 发送消息到MQ
     * @param pageId
     */

    private void sendMsgToMq(String pageId) {
        //获取cmsPage
        CmsPage cmsPage = this.findById(pageId);
        if(null == cmsPage){
            ExceptionCast.cast(CommonCode.INVALIDPARAM);
        }
        //拼接消息内容
        Map<String,String> msg = new HashMap<>();
        msg.put("pageId", pageId);
        String jsonString = JSON.toJSONString(msg);
        rabbitTemplate.convertAndSend(RabbitmqConfig.EX_ROUTING_CMS_POSTPAGE,cmsPage.getSiteId(),jsonString);
    }


    /***
     * 保存内容到gridfs,并更新cmspage的htmlfileId字段
     * @param pageId
     * @param pageHtml
     * @return
     */
    private CmsPage saveHtml(String pageId, String pageHtml) {
        InputStream inputStream = null;
        //获取cmsPage
        CmsPage cmsPage = this.findById(pageId);
        if(null == cmsPage){
            ExceptionCast.cast(CommonCode.INVALIDPARAM);
        }
        //获取页面名称
        String pageName = cmsPage.getPageName();
        try {
            //获取输入流
            inputStream = IOUtils.toInputStream(pageHtml, "utf-8");
            ObjectId objectId = gridFsTemplate.store(inputStream, pageName);
            //更新到cmsPage
            cmsPage.setHtmlFileId(objectId.toHexString());
            CmsPage save = cmsPageRepository.save(cmsPage);
            return save;
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(null != inputStream){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

}
